/********************************** (C) COPYRIGHT *******************************
* File Name          : peripheral.C
* Author             : zhangxiyi @WCH
* Version            : v0.1
* Date               : 2020/08/07
* Description        :
*******************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "CONFIG.h"
#include "CH57x_common.h"
//#include "devinfoservice.h"
#include "gattprofile.h"
#include "peripheral.h"

#include "ble_uart_service.h"


uint8 Peripheral_TaskID = INVALID_TASK_ID;   // Task ID for internal task/event processing



void hex_dump(void *data,uint32_t length) {
    uint8_t *p;
    p = (uint8_t *)data;
    for(uint32_t i=0;i<length;i++) {
        PRINT("%02x ",*p);
        p++;
    }
    PRINT("\r\n");
}


//ble uart service callback handler
void on_bleuartServiceEvt(uint16_t connection_handle,ble_uart_evt_t  *p_evt) {
    uint8_t result;
    switch(p_evt->type) {
    case BLE_UART_EVT_TX_NOTI_DISABLED:
        PRINT("%02x:bleuart_EVT_TX_NOTI_DISABLED\r\n",connection_handle);
        break;
    case BLE_UART_EVT_TX_NOTI_ENABLED :
        PRINT("%02x:bleuart_EVT_TX_NOTI_ENABLED\r\n",connection_handle);
        break;
    case BLE_UART_EVT_BLE_DATA_RECIEVED:
        PRINT("BLE RX len:%d\r\n",p_evt->data.length);

        hex_dump((uint8_t *)p_evt->data.p_data,p_evt->data.length);

        result = ble_send(connection_handle,(uint8_t *)p_evt->data.p_data,p_evt->data.length);
        if(result) {
            PRINT("ble send failed:%02x \n",result);
        }else{
            PRINT("ble send success \n");
        }


        break;
    default:
        break;
    }
}




/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

// How often to perform periodic event
#define SBP_PERIODIC_EVT_PERIOD               1600

// How often to perform read rssi event
#define SBP_READ_RSSI_EVT_PERIOD              3200

// Parameter update delay
#define SBP_PARAM_UPDATE_DELAY                6400

// What is the advertising interval when device is discoverable (units of 625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL          1600

// Limited discoverable mode advertises for 30.72s, and then stops
// General discoverable mode advertises indefinitely
#define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL

// Minimum connection interval (units of 1.25ms, 10=12.5ms)
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL     6

// Maximum connection interval (units of 1.25ms, 100=125ms)
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL     100

// Slave latency to use parameter update
#define DEFAULT_DESIRED_SLAVE_LATENCY         0

// Supervision timeout value (units of 10ms, 100=1s)
#define DEFAULT_DESIRED_CONN_TIMEOUT          100

// Company Identifier: WCH
#define WCH_COMPANY_ID                        0x07D7


/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */




//static uint8 Peripheral_TaskID = INVALID_TASK_ID;   // Task ID for internal task/event processing

// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[ ] = {
    // complete name
    15,   // length of this data
    GAP_ADTYPE_LOCAL_NAME_COMPLETE,
    'c','h','5','7','x','_','b','l','e','_','u','a','r','t',
    // connection interval range
    0x05,   // length of this data
    GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
    LO_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),   // 100ms
    HI_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),
    LO_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),   // 1s
    HI_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),

    // Tx power level
    0x02,   // length of this data
    GAP_ADTYPE_POWER_LEVEL,
    0       // 0dBm
};

// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertisting)
static uint8 advertData[] = {
    // Flags; this sets the device to use limited discoverable
    // mode (advertises for 30 seconds at a time) instead of general
    // discoverable mode (advertises indefinitely)
    0x02,   // length of this data
    GAP_ADTYPE_FLAGS,
    DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,

    // service UUID, to notify central devices what services are included
    // in this peripheral
    0x03,   // length of this data
    GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all
    LO_UINT16( SIMPLEPROFILE_SERV_UUID ),
    HI_UINT16( SIMPLEPROFILE_SERV_UUID )
};

// GAP GATT Attributes
static uint8 attDeviceName[GAP_DEVICE_NAME_LEN] = "ch57x_ble_uart";

// Connection item list
static peripheralConnItem_t peripheralConnList;

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void Peripheral_ProcessTMOSMsg( tmos_event_hdr_t *pMsg );
static void peripheralStateNotificationCB( gapRole_States_t newState,gapRoleEvent_t * pEvent);


static void peripheralParamUpdateCB( uint16 connHandle, uint16 connInterval,
                                     uint16 connSlaveLatency, uint16 connTimeout );
static void peripheralInitConnItem( peripheralConnItem_t* peripheralConnList );
static void peripheralRssiCB( uint16 connHandle, int8  rssi );


/*********************************************************************
 * PROFILE CALLBACKS
 */

// GAP Role Callbacks
static gapRolesCBs_t Peripheral_PeripheralCBs = {
    peripheralStateNotificationCB,  // Profile State Change Callbacks
    peripheralRssiCB,                   // When a valid RSSI is read from controller (not used by application)
    peripheralParamUpdateCB
};

// Broadcast Callbacks
static gapRolesBroadcasterCBs_t Broadcaster_BroadcasterCBs = {
    NULL,  										// Not used in peripheral role
    NULL											// Receive scan request callback
};

// GAP Bond Manager Callbacks
static gapBondCBs_t Peripheral_BondMgrCBs = {
    NULL,                     // Passcode callback (not used by application)
    NULL                      // Pairing / Bonding state Callback (not used by application)
};


/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      Peripheral_Init
 *
 * @brief   Initialization function for the Peripheral App Task.
 *          This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notificaiton ... ).
 *
 * @param   task_id - the ID assigned by TMOS.  This ID should be
 *                    used to send messages and set timers.
 *
 * @return  none
 */
void Peripheral_Init( ) {
    Peripheral_TaskID = TMOS_ProcessEventRegister( Peripheral_ProcessEvent );

    // Setup the GAP Peripheral Role Profile
    {
        uint8 initial_advertising_enable = TRUE;
        uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;

        // Set the GAP Role Parameters
        GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );
        GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, sizeof ( scanRspData ), scanRspData );
        GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData );
        GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );
        GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );
    }

    // Set the GAP Characteristics
    GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName );

    // Set advertising interval
    {
        uint16 advInt = DEFAULT_ADVERTISING_INTERVAL;

        GAP_SetParamValue( TGAP_DISC_ADV_INT_MIN, advInt );
        GAP_SetParamValue( TGAP_DISC_ADV_INT_MAX, advInt );
    }

    // Setup the GAP Bond Manager
    {
        uint32 passkey = 0; // passkey "000000"
        uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8 mitm = TRUE;
        uint8 bonding = TRUE;
        uint8 ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        GAPBondMgr_SetParameter( GAPBOND_PERI_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );
        GAPBondMgr_SetParameter( GAPBOND_PERI_PAIRING_MODE, sizeof ( uint8 ), &pairMode );
        GAPBondMgr_SetParameter( GAPBOND_PERI_MITM_PROTECTION, sizeof ( uint8 ), &mitm );
        GAPBondMgr_SetParameter( GAPBOND_PERI_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );
        GAPBondMgr_SetParameter( GAPBOND_PERI_BONDING_ENABLED, sizeof ( uint8 ), &bonding );
    }

    // Initialize GATT attributes
    //GGS_AddService( GATT_ALL_SERVICES );            // GAP
    //GATTServApp_AddService( GATT_ALL_SERVICES );    // GATT attributes
    //DevInfo_AddService();                           // Device Information Service
    ble_uart_add_service(on_bleuartServiceEvt);

    // Init Connection Item
    peripheralInitConnItem( &peripheralConnList );



    // Register receive scan request callback
    GAPRole_BroadcasterSetCB( &Broadcaster_BroadcasterCBs );

    // Setup a delayed profile startup
    tmos_set_event( Peripheral_TaskID, SBP_START_DEVICE_EVT );
}

/*********************************************************************
 * @fn      peripheralInitConnItem
 *
 * @brief   Init Connection Item
 *
 * @param   peripheralConnList -
 *
 * @return  NULL
 */
static void peripheralInitConnItem( peripheralConnItem_t* peripheralConnList ) {
    peripheralConnList->connHandle = GAP_CONNHANDLE_INIT;
    peripheralConnList->connInterval = 0;
    peripheralConnList->connSlaveLatency = 0;
    peripheralConnList->connTimeout = 0;
}



/*********************************************************************
 * @fn      Peripheral_ProcessEvent
 *
 * @brief   Peripheral Application Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 *
 * @param   task_id - The TMOS assigned task ID.
 * @param   events - events to process.  This is a bit map and can
 *                   contain more than one event.
 *
 * @return  events not processed
 */
uint16 Peripheral_ProcessEvent( uint8 task_id, uint16 events ) {
    static  attHandleValueNoti_t noti;
//  VOID task_id; // TMOS required parameter that isn't used in this function

    if ( events & SYS_EVENT_MSG ) {
        uint8 *pMsg;

        if ( (pMsg = tmos_msg_receive( Peripheral_TaskID )) != NULL ) {
            Peripheral_ProcessTMOSMsg( (tmos_event_hdr_t *)pMsg );
            // Release the TMOS message
            tmos_msg_deallocate( pMsg );
        }
        // return unprocessed events
        return (events ^ SYS_EVENT_MSG);
    }

    if ( events & SBP_START_DEVICE_EVT ) {
        // Start the Device
        GAPRole_PeripheralStartDevice( Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs );
        return ( events ^ SBP_START_DEVICE_EVT );
    }
    if ( events & SBP_PARAM_UPDATE_EVT ) {
        // Send connect param update request
        GAPRole_PeripheralConnParamUpdateReq( peripheralConnList.connHandle,
                                              12,
                                              12,
                                              DEFAULT_DESIRED_SLAVE_LATENCY,
                                              DEFAULT_DESIRED_CONN_TIMEOUT,
                                              Peripheral_TaskID);
        return (events ^ SBP_PARAM_UPDATE_EVT);
    }

    if(events & UART_TO_BLE_SEND_EVT) {

        return (events ^ UART_TO_BLE_SEND_EVT);
    }
    // Discard unknown events
    return 0;
}

/*********************************************************************
 * @fn      Peripheral_ProcessTMOSMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void Peripheral_ProcessTMOSMsg( tmos_event_hdr_t *pMsg ) {
    switch ( pMsg->event ) {
    default:
        break;
    }
}

/*********************************************************************
 * @fn      Peripheral_LinkEstablished
 *
 * @brief   Process link established.
 *
 * @param   pEvent - event to process
 *
 * @return  none
 */
static void Peripheral_LinkEstablished( gapRoleEvent_t * pEvent ) {
    gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *) pEvent;

    // See if already connected
    if( peripheralConnList.connHandle != GAP_CONNHANDLE_INIT ) {
        GAPRole_TerminateLink( pEvent->linkCmpl.connectionHandle );
        PRINT( "Connection max...\n" );
    } else {
        peripheralConnList.connHandle = event->connectionHandle;
        peripheralConnList.connInterval = event->connInterval;
        peripheralConnList.connSlaveLatency = event->connLatency;
        peripheralConnList.connTimeout = event->connTimeout;



        // Set timer for param update event
        tmos_start_task( Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY );

        PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval);
    }
}

/*********************************************************************
 * @fn      Peripheral_LinkTerminated
 *
 * @brief   Process link terminated.
 *
 * @param   pEvent - event to process
 *
 * @return  none
 */
static void Peripheral_LinkTerminated( gapRoleEvent_t * pEvent ) {
    gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *) pEvent;

    if( event->connectionHandle == peripheralConnList.connHandle ) {
        peripheralConnList.connHandle = GAP_CONNHANDLE_INIT;
        peripheralConnList.connInterval = 0;
        peripheralConnList.connSlaveLatency = 0;
        peripheralConnList.connTimeout = 0;

        // Restart advertising
        {

            uint8 advertising_enable = TRUE;
            GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &advertising_enable );
        }
    } else {
        PRINT("ERR..\n");
    }
}

/*********************************************************************
 * @fn      peripheralRssiCB
 *
 * @brief   RSSI callback.
 *
 * @param   connHandle - connection handle
 * @param   rssi - RSSI
 *
 * @return  none
 */
static void peripheralRssiCB( uint16 connHandle, int8 rssi ) {
    PRINT( "RSSI -%d dB Conn  %x \n", -rssi, connHandle);
}

/*********************************************************************
 * @fn      peripheralParamUpdateCB
 *
 * @brief   Parameter update complete callback
 *
 * @param   connHandle - connect handle
 *          connInterval - connect interval
 *          connSlaveLatency - connect slave latency
 *          connTimeout - connect timeout
 *
 * @return  none
 */
static void peripheralParamUpdateCB( uint16 connHandle, uint16 connInterval,
                                     uint16 connSlaveLatency, uint16 connTimeout ) {
    if( connHandle == peripheralConnList.connHandle ) {
        peripheralConnList.connInterval = connInterval;
        peripheralConnList.connSlaveLatency = connSlaveLatency;
        peripheralConnList.connTimeout = connTimeout;

        PRINT("Update %x - Int %x \n", connHandle, connInterval);
    } else {
        PRINT("peripheralParamUpdateCB err..\n");
    }
}

/*********************************************************************
 * @fn      peripheralStateNotificationCB
 *
 * @brief   Notification from the profile of a state change.
 *
 * @param   newState - new state
 *
 * @return  none
 */
static void peripheralStateNotificationCB( gapRole_States_t newState, gapRoleEvent_t * pEvent ) {
    switch ( newState ) {
    case GAPROLE_STARTED:
        PRINT( "Initialized..\n" );
        break;

    case GAPROLE_ADVERTISING:
        if( pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT ) {
            Peripheral_LinkTerminated( pEvent );

        }
        PRINT( "Advertising..\n" );
        break;

    case GAPROLE_CONNECTED:
        if( pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT ) {
            Peripheral_LinkEstablished( pEvent );
            PRINT( "Connected..\n" );
        }
        break;

    case GAPROLE_CONNECTED_ADV:
        PRINT( "Connected Advertising..\n" );
        break;

    case GAPROLE_WAITING:
        if( pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT ) {
            PRINT( "Waiting for advertising..\n" );
        } else if( pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT ) {
            Peripheral_LinkTerminated( pEvent );
            PRINT( "Disconnected.. Reason:%x\n",pEvent->linkTerminate.reason );
        } else if( pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT ) {
            if( pEvent->gap.hdr.status != SUCCESS ) {
                PRINT( "Waiting for advertising..\n" );
            } else {
                PRINT( "Error..\n" );
            }
        } else {
            PRINT( "Error..%x\n",pEvent->gap.opcode );
        }
        break;

    case GAPROLE_ERROR:
        PRINT( "Error..\n" );
        break;

    default:
        break;
    }
}






/*********************************************************************
*********************************************************************/
